﻿namespace System.Diagnostics
{
    /// <summary>
    /// 	Stopwatch is used to measure the general performance of Silverlight functionality. Silverlight does not provide a high resolution timer as is available in many operating systems, so the resolution of this timer is limited to "ticks." This class is best used to measure the relative performance of functions over many iterations.
    /// </summary>
    public class Stopwatch
    {
        #region Static Operations

        //[DllImport("Kernel32.dll")]
        //private static extern bool QueryPerformanceCounter(out long performanceCount);

        //[DllImport("Kernel32.dll")]
        //private static extern bool QueryPerformanceFrequency(out long frequency);

        #region Fields & Properties

        /// <summary>
        /// 	Gets the frequency of the timer as the number of ticks per second. This field is read-only.
        /// </summary>
        public static readonly long Frequency;

        /// <summary>
        /// 	Indicates whether the timer is based on a high-resolution performance counter. This field is read-only.
        /// </summary>
        public static readonly bool IsHighResolution;

        private static readonly double s_TickFrequency;

        #endregion

        #region Constructors & Destructors

        static Stopwatch()
        {
            //if (!QueryPerformanceFrequency(out Frequency))
            //{
            //    IsHighResolution = false;
            //    Frequency = TimeSpan.TicksPerSecond;
            //    s_TickFrequency = 1.0;
            //}
            //else
            //{
            //    IsHighResolution = true;
            //    s_TickFrequency = TimeSpan.TicksPerSecond;
            //    s_TickFrequency /= Frequency;
            //}
            IsHighResolution = false;
            Frequency = TimeSpan.TicksPerSecond;
            s_TickFrequency = 1.0;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// 	Initializes a new stopwatch instance, sets the elapsed time property to zero, and starts measuring elapsed time.
        /// </summary>
        /// <returns> A stopwatch that has just begun measuring elapsed time. </returns>
        public static Stopwatch StartNew()
        {
            var stopwatch = new Stopwatch();
            stopwatch.Start();
            return stopwatch;
        }

        /// <summary>
        /// 	Gets the current number of ticks in the timer mechanism.
        /// </summary>
        /// <returns> A long integer representing the tick counter value of the underlying timer mechanism. </returns>
        public static long GetTimestamp()
        {
            //if (IsHighResolution)
            //{
            //long timestamp;
            //Win32Native.QueryPerformanceCounter(out timestamp);
            //return timestamp;
            //}
            return DateTime.UtcNow.Ticks;
        }

        #endregion

        #endregion

        #region Fields & Properties

        private long _elapsed;
        private bool _isRunning;
        private long _startTimeStamp;

        /// <summary>
        /// 	Gets a value indicating whether the stopwatch timer is running.
        /// </summary>
        /// <returns> true if the stopwatch instance is currently running and measuring elapsed time for an interval; otherwise, false. </returns>
        public bool IsRunning
        {
            get
            {
                return _isRunning;
            }
        }

        /// <summary>
        /// 	Gets the total elapsed time measured by the current instance.
        /// </summary>
        /// <returns> A read-only TimeSpan representing the total elapsed time measured by the current instance. </returns>
        public TimeSpan Elapsed
        {
            get
            {
                return new TimeSpan(GetElapsedDateTimeTicks());
            }
        }

        /// <summary>
        /// 	Gets the total elapsed time measured by the current instance, in milliseconds.
        /// </summary>
        /// <returns> A read-only long integer representing the total number of milliseconds measured by the current instance. </returns>
        public long ElapsedMilliseconds
        {
            get
            {
                return (GetElapsedDateTimeTicks() / TimeSpan.TicksPerMillisecond);
            }
        }

        /// <summary>
        /// 	Gets the total elapsed time measured by the current instance, in timer ticks.
        /// </summary>
        /// <returns> A read-only long integer representing the total number of timer ticks measured by the current instance. </returns>
        public long ElapsedTicks
        {
            get
            {
                return GetRawElapsedTicks();
            }
        }

        #endregion

        #region Constructors & Destructors

        /// <summary>
        /// 	Initializes a new instance of the stopwatch class.
        /// </summary>
        public Stopwatch()
        {
            Reset();
        }

        #endregion

        #region Run

        /// <summary>
        /// 	Starts, or resumes, measuring elapsed time for an interval.
        /// </summary>
        public void Start()
        {
            if (!_isRunning)
            {
                _startTimeStamp = GetTimestamp();
                _isRunning = true;
            }
        }

        /// <summary>
        /// 	Stops measuring elapsed time for an interval.
        /// </summary>
        public void Stop()
        {
            if (_isRunning)
            {
                _elapsed += (GetTimestamp() - _startTimeStamp);
                _isRunning = false;
                if (_elapsed < 0)
                {
                    _elapsed = 0;
                }
            }
        }

        /// <summary>
        /// 	Stops time interval measurement, resets the elapsed time to zero, and starts measuring elapsed time.
        /// </summary>
        public void Restart()
        {
            _elapsed = 0L;
            _startTimeStamp = GetTimestamp();
            _isRunning = true;
        }

        /// <summary>
        /// 	Stops time interval measurement and resets the elapsed time to zero.
        /// </summary>
        public void Reset()
        {
            _elapsed = 0;
            _isRunning = false;
            _startTimeStamp = 0;
        }

        #endregion

        #region Get Ticks

        private long GetElapsedDateTimeTicks()
        {
            var rawElapsedTicks = GetRawElapsedTicks();
            if (IsHighResolution)
            {
                return (long) (rawElapsedTicks * s_TickFrequency);
            }
            return rawElapsedTicks;
        }

        private long GetRawElapsedTicks()
        {
            var elapsed = _elapsed;
            if (_isRunning)
            {
                elapsed += (GetTimestamp() - _startTimeStamp);
            }
            return elapsed;
        }

        #endregion
    }
}